package com.top.up.biz;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.top.up.constants.WxMiniAPI;
import com.top.up.core.config.redis.RedisCommand;
import com.top.up.core.dto.AppConfigDTO;
import com.top.up.core.dto.oauth.AccessTokenDTO;
import com.top.up.core.dto.oauth.UserInfo;
import com.top.up.core.dto.oauth.WxOauthDTO;
import com.top.up.core.dto.oauth.WxOauthReqDTO;
import com.top.up.core.enums.HttpEnum;
import com.top.up.core.enums.KeyEnum;
import com.top.up.core.execption.CommonException;
import com.top.up.entity.UserEntity;
import com.top.up.enums.ResultEnum;
import com.top.up.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Component
public class WXOauthBiz {

    @Resource
    private AppBiz appBiz;
    @Resource
    private RedisCommand redisCommand;
    @Resource
    private UserService userService;

    private ReentrantLock lock = new ReentrantLock();

    /**
     * 小程序全局唯一后台接口调用凭据
     *
     * @param channelCode
     * @return
     */
    public AccessTokenDTO token(String channelCode) {
        String key = KeyEnum.TOKEN_KEY.getKey() + channelCode;
        try {
            lock.lock();
            String token = redisCommand.get(key);
            if(StringUtils.isNotBlank(token)){
                JSONObject jsonObject = JSONObject.parseObject(token);
                return jsonObject.toJavaObject(AccessTokenDTO.class);
            }

            AppConfigDTO info = appBiz.info(channelCode);
            log.info("微信小程序应用配置参数：{}", JSON.toJSONString(info));
            if(Objects.isNull(info)){
                throw new CommonException(ResultEnum.ERROR.getCode(), "应用[AppId, AppSecret]不能为空");
            }

            String oauthURL = WxMiniAPI.AUTH_ACCESSTOKEN_GET.replace("APPID", info.getAppKey())
                    .replace("APPSECRET", info.getAppSecret());

            String result = HttpUtil.get(oauthURL);
            log.info("请求[微信小程序]全局唯一后台接口调用凭据返回：{}", result);
            if( Objects.isNull(result) ){
                throw new CommonException(ResultEnum.ERROR.getCode(), "获取小程序全局唯一后台接口调用凭据失败");
            }

            JSONObject resultJSON = JSONObject.parseObject(result);
            int errcode = resultJSON.getIntValue("errcode");
            if( HttpEnum.OK.getCode() == errcode ){
                AccessTokenDTO accessToken = AccessTokenDTO.builder()
                        .access_token(resultJSON.getString("access_token"))
                        .expires_in(resultJSON.getLongValue("expires_in"))
                        .build();
                // 缓存
                redisCommand.set(key, JSON.toJSONString(accessToken), accessToken.getExpires_in() - 200);
                return accessToken;
            }
            // 获取错误，则抛出异常信息
            String msg = HttpEnum.get(errcode).getMsg();
            log.error("[微信小程序]获取后台接口调用凭据异常：[code = {}, msg = {}]", errcode, msg);
            throw new CommonException(ResultEnum.ERROR.getCode(), msg);
        }catch (Exception e){
            log.error("[微信小程序]请求接口调用凭据异常：", e);
        }finally {
            lock.unlock();
        }

        return null;
    }

    /**
     * 小程序登录，通过临时登录code获取openId
     *
     * @param channelCode
     * @param wxOauthReq
     * @return
     */
    public WxOauthDTO auth2Session(String channelCode, WxOauthReqDTO wxOauthReq) {

        // 通过临时登录CODE获取用户openId
        String result = getOpenId(channelCode, wxOauthReq.getCode());

        JSONObject resultJSON = JSONObject.parseObject(result);
        int errCode = resultJSON.getIntValue("errcode");
        if( HttpEnum.OK.getCode() == errCode ){
            WxOauthDTO wxOauth = WxOauthDTO.builder()
                    .openid(resultJSON.getString("openid"))
                    .build();

            UserEntity entity = userService.getOne(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getOpenId, wxOauth.getOpenid()));
            if( Objects.nonNull(entity) ){
                UserInfo userInfo = UserInfo.builder().build();
                BeanUtils.copyProperties(entity, userInfo);
                wxOauth.setUserInfo(userInfo);
            }
            return wxOauth;
        }

        return null;
    }

    private String getOpenId(String channelCode, String code) {
        AppConfigDTO info = appBiz.info(channelCode);
        log.info("微信小程序应用配置参数：{}", JSON.toJSONString(info));
        if(Objects.isNull(info)){
            throw new CommonException(ResultEnum.ERROR.getCode(), "应用[AppId, AppSecret]不能为空");
        }

        String auth2SessionURL = WxMiniAPI.AUTH_CODE2SESSION_GET.replace("APPID", info.getAppKey())
                .replace("SECRET", info.getAppSecret()).replace("JSCODE", code);

        String result = HttpUtil.get(auth2SessionURL);
        log.info("[登录凭证] 请求登录校验返回结果：{}", result);
        return result;
    }

    /**
     * 用户授权，获取基本信息
     *
     * @param channelCode
     * @param wxOauthReq
     * @return
     */
    public WxOauthDTO authUserInfo(String channelCode, WxOauthReqDTO wxOauthReq) {
        // 通过临时登录CODE获取用户openId
        String result = getOpenId(channelCode, wxOauthReq.getCode());

        JSONObject resultJSON = JSONObject.parseObject(result);
        int errCode = resultJSON.getIntValue("errcode");
        if( HttpEnum.OK.getCode() == errCode ){
            WxOauthDTO wxOauth = WxOauthDTO.builder()
                    .openid(resultJSON.getString("openid"))
                    .userInfo(wxOauthReq.getUserInfo())
                    .build();

            if( Objects.nonNull(wxOauthReq.getUserInfo()) ){
                UserInfo userInfo = wxOauthReq.getUserInfo();
                userInfo.setOpenId(wxOauth.getOpenid());
                UserEntity entity = userService.getOne(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getOpenId, wxOauth.getOpenid()));
                // 不存在，则第一次授权获取用户信息
                if( Objects.isNull(entity) ){
                    entity = UserEntity.builder().build();
                    BeanUtils.copyProperties(userInfo, entity);
                    userService.save(entity);
                }else{
                    // 存在则，多次授权获取用户信息，更新数据库数据
                    UserEntity userEntity = UserEntity.builder().build();
                    BeanUtils.copyProperties(userInfo, entity);
                    userEntity.setId(entity.getId());
                    userService.updateById(userEntity);
                }
                wxOauth.setUserInfo(userInfo);
            }
            return wxOauth;
        }

        return null;
    }
}
